package com.forest.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.Digester;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.forest.dto.SignatureDTO;
import com.forest.dto.ValidateSignatureDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

/**
 * 签名工具类.
 */
public enum SignUtil {
    INSTANCE;

    private static final Logger log = LoggerFactory.getLogger(SignUtil.class);

    public static SignUtil getInstance() {
        return INSTANCE;
    }

    /**
     * 业务参数.
     */
    private static final String EXTRA = "extra";

    /**
     * 签名字段.
     */
    private static final String SIGNATURE = "signature";

    /** 时间戳字段. */
    private static final String TIMESTAMP = "timestamp";

    /** 签名有效期字段. */
    private static final String EXPIRE_SECONDS = "expireSeconds";

    /** 签名算法字段. */
    private static final String SIGNATURE_ALGORITHM = "signatureAlgorithm";


    /**
     * 签名
     *
     * @param params
     * @return
     */
    public String sign(Map<String, Object> params) {
        String algorithm = (String) params.getOrDefault(SIGNATURE_ALGORITHM, "SHA256");
        params.remove(SIGNATURE_ALGORITHM);
        return signHex(params, algorithm);
    }

    /**
     * 签名.
     *
     * @param dto
     * @return
     */
    public String sign(SignatureDTO dto) {
        Map<String, Object> params = getParams(dto);
        params.remove(SIGNATURE_ALGORITHM);
        return signHex(params, dto.getSignatureAlgorithm());
    }

    /**
     * 签名
     *
     * @param params
     * @return
     */
    public String signHex(Map<String, Object> params, String algorithm) {
        String signStr = getSignStr(params);
        log.info("签名字符串: {}", signStr);
        Digester digester = DigestUtil.digester(algorithm);
        return digester.digestHex(signStr);
    }




    /**
     * 获取签名字符串.(自然排序)
     * @param params
     * @return
     */
    private String getSignStr(Map<String, Object> params) {
        StringBuilder sbf = new StringBuilder();
        TreeMap<String, Object> treeMap = MapUtil.sort(params, Comparator.naturalOrder());
        treeMap.forEach((k, v) -> {
            if(sbf.length() > 0) {
                sbf.append("&");
            }
            //拼接签名字符串
            sbf.append(k).append("=").append(v);
        });
        return sbf.toString();
    }


    /**
     * 验证签名.
     *
     * @param dto
     * @return
     */
    public boolean verify(ValidateSignatureDTO dto) {
        Map<String, Object> params = getParams(dto);
        return verify(params);
    }

    /**
     * 处理对象参数，为map
     * @param t
     * @param <T>
     * @return
     */
    private <T> Map<String, Object> getParams(T t) {
        Map<String, Object> params = BeanUtil.beanToMap(t);
        String extra = (String)params.getOrDefault(EXTRA, "");
        //移除extra字段
        params.remove(EXTRA);
        //处理业务参数，添加到签名参数中
        JSONObject jsonObject = JSONUtil.parseObj(extra);
        if(jsonObject.size() > 0) {
            params.putAll(jsonObject);
        }
        return params;
    }

    /**
     * 验证签名
     * @param params
     * @return
     */
    public boolean verify(Map<String, Object> params) {
        String timestamp = (String) params.getOrDefault(TIMESTAMP, "0");
        long expireSeconds = (Long)params.getOrDefault(EXPIRE_SECONDS, "300");
        Assert.isTrue((System.currentTimeMillis()/1000 - Long.valueOf(timestamp)) <= expireSeconds, "无效签名时间!");
        String signature = (String) params.getOrDefault(SIGNATURE, "");
        //移除签名字段
        params.remove(SIGNATURE);
        params.remove(EXPIRE_SECONDS);
        String sign = sign(params);
        return StrUtil.equals(sign, signature);
    }

}
